use std::io;
mod local;
mod ufile;

pub trait FS {
    fn read(&self, name: &str) -> Result<String, io::Error>;
    fn read_to_writer(&self, name: &str, writer: &mut dyn io::Write) -> io::Result<()>;
    fn write(&self, name: &str, content: &str) -> io::Result<()>;
    fn write_from_reader(&self, name: &str, r: &mut dyn io::Read) -> io::Result<()>;
    fn delete(&self, name: &str) -> io::Result<()>;
}

pub enum Type {
    Local(local::Config),
    Ufile(ufile::Config),
}

pub fn from_new<T: From<Type>>(cfg: Type) -> T {
    From::from(cfg)
}

pub fn new(cfg: Type) -> Box<dyn FS> {
    match cfg {
        Type::Ufile(cfg) => {
            return Box::new(ufile::Client::new(cfg));
        }
        Type::Local(cfg) => {
            return Box::new(local::Client::new(cfg));
        }
    }
}

#[cfg(test)]
mod test {
    // use super::{local, new, new_box, ufile, Type, FS};
    use crate::fs::{from_new, local, new, ufile, Type, FS};
    use std::fs;

    #[test]
    fn ufile() {
        let cfg = ufile::Config {
            addr: "127.0.0.1".to_string(),
        };
        let c = ufile::Client::new(cfg);

        let name = "ufile-test";
        assert_eq!("ufile content".to_string(), c.read(name).unwrap());

        let mut w: Vec<u8> = vec![];
        c.read_to_writer(name, &mut w).unwrap();
        assert_eq!(name.to_string(), String::from_utf8(w).unwrap())
    }

    #[test]
    fn fs_ufile() {
        let fs: ufile::Client = from_new(Type::Ufile(ufile::Config {
            addr: "127.0.0.1".to_string(),
        }));

        let name = "fs-ufile-test";
        assert_eq!("ufile content".to_string(), fs.read(name).unwrap());

        let mut w: Vec<u8> = vec![];
        fs.read_to_writer(name, &mut w).unwrap();
        assert_eq!(name.to_string(), String::from_utf8(w).unwrap())
    }

    #[test]
    fn fs_local() {
        let path = "data/fs";
        let name = "fs-local-test";
        let content = "fs-local-test content";

        fs::create_dir_all(path).unwrap();

        let cfg = local::Config {
            path: path.to_string(),
        };
        let fs = new(Type::Local(cfg));

        if let Err(_) = fs.delete(name) {};
        fs.write(name, content).unwrap();
        assert_eq!(content.to_string(), fs.read(name).unwrap());

        if let Err(_) = fs.delete(name) {};
        fs.write_from_reader(name, &mut content.as_bytes()).unwrap();
        assert_eq!(content.to_string(), fs.read(name).unwrap());

        let mut w: Vec<u8> = vec![];
        fs.read_to_writer(name, &mut w).unwrap();
        assert_eq!(content.to_string(), String::from_utf8(w).unwrap());

        fs.delete(name).unwrap();
    }
}
